/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package fi.uba.algo3.tp3.modelo.actores;

import ar.uba.fi.algo3.titiritero.Posicionable;
import fi.uba.algo3.tp3.modelo.servicios.ServicioJuego;
import fi.uba.algo3.tp3.modelo.inanimados.Obstaculo;
import fi.uba.algo3.tp3.utils.Punto;
import java.util.Vector;

/**
 *
 * @author Familia
 */
public class FantasmaListo extends Fantasma {

    public FantasmaListo (ServicioJuego servicio, Integer distanciaDeColision) {
        super(servicio, distanciaDeColision);
    }

    @Override
    /* Mueve el fantasma acercandose al pacman en forma aleatoria. Este metodo */
    /* asume que al invocarse el pacman y el fantasma están en distinta posicion. */
    protected void vivirAtacando(ServicioJuego servicio) {
        this.moverParaAcercarceAlPacman(servicio);
    }

    @Override
    /* Mueve el fantasma huyendo del pacman. Este metodo asume que al invocarse */
    /* el pacman y el fantasma están en distinta posicion. */
    protected void vivirHuyendo(ServicioJuego servicio) {
        moverParaAlejarseDelPacman(servicio);
    }

    @Override
    /* Mueve el fantasma en direccion a la jaula. */
    protected void vivirComido() {
        int distanciaHorizontal = this.getX() - posicionInicialX;
        int distanciaVertical = this.getY() - posicionInicialY;
        if (Math.abs(distanciaHorizontal) > Math.abs(distanciaVertical)) {
            moverHorizontalmente(distanciaHorizontal);
        } else {
            moverVerticalmente(distanciaVertical);
        }
        //cuando van a la jaula no importa si pasan por encima de los obstaculos
        this.x += sentidoX;
        this.y += sentidoY;
    }

    private Vector<Punto> obtenerListaDePuntosPorMenorDistanciaAlPacman(Pacman pacman) {
        Punto p1,p2,p3,p4;

        p1 = new Punto(this.x + 5, this.y);
        p2 = new Punto(this.x - 5, this.y);
        p3 = new Punto(this.x, this.y + 5);
        p4 = new Punto(this.x, this.y - 5);

        int d1,d2,d3,d4;

        d1 = calcularDistanciaAlPosicionable(pacman,p1);
        d2 = calcularDistanciaAlPosicionable(pacman,p2);
        d3 = calcularDistanciaAlPosicionable(pacman,p3);
        d4 = calcularDistanciaAlPosicionable(pacman,p4);

        Vector<Punto> puntosMasCercanos = new Vector<Punto>();

        puntosMasCercanos.add(p1);

        if (d2 < d1) {
            puntosMasCercanos.insertElementAt(p2, 0);
        }
        else {
            puntosMasCercanos.add(p2);
        }


        if (d3 < d1) {
            puntosMasCercanos.insertElementAt(p3, 0);
        }
        else if (d3 < d2) {
            puntosMasCercanos.insertElementAt(p3, 1);
        }
        else {
            puntosMasCercanos.add(p3);
        }

        if (d4 < d1) {
            puntosMasCercanos.insertElementAt(p4, 0);
        }
        else if (d4 < d2) {
            puntosMasCercanos.insertElementAt(p4, 1);
        }
        else if (d4 < d3) {
            puntosMasCercanos.insertElementAt(p4, 2);
        }
        else {
            puntosMasCercanos.add(p3);
        }

        return puntosMasCercanos;
    }

    private void moverParaAcercarceAlPacman(ServicioJuego servicio) {
        Obstaculo obstaculo=null;
        Pacman pacman = servicio.getPacman();
        int distanciaInicial = this.calcularDistanciaAlPosicionable(pacman, new Punto(x,y));
        Vector<Punto> puntosMasCercanos = obtenerListaDePuntosPorMenorDistanciaAlPacman(pacman);

        for (int i = 0; i < 4; i ++)  {
            this.moverHaciaElPunto(puntosMasCercanos.get(i));
            this.x += sentidoX;
            this.y += sentidoY;
            obstaculo = servicio.obtenerObstaculoMasCercano(this, distanciaDeColision.doubleValue());
            if (obstaculo == null) {
                int distanciaActual = this.calcularDistanciaAlPosicionable(pacman, new Punto(x,y));
                if (this.meAproximeAlPacman(distanciaInicial, distanciaActual)) {
                    break;
                } else {
                    this.revertirMovimiento();
                }
            } else {
                this.revertirMovimiento();
            }            
        }
    }

    private void moverParaAlejarseDelPacman(ServicioJuego servicio) {
        int posXInicial = this.x;
        int posYInicial = this.y;

        int sentido = 0;
        Pacman pacman = servicio.getPacman();
        int distanciaInicial = this.calcularDistanciaAlPosicionable(pacman, new Punto(x,y));
        int distanciaFinal = distanciaInicial;
        while( !meAlejeDelPacman(distanciaInicial, distanciaFinal)) {
            sentido++;
            if (sentido > 1)
                this.revertirMovimiento();
            this.moverSegunSentido(sentido);
            this.x += sentidoX;
            this.y += sentidoY;
            distanciaFinal = this.calcularDistanciaAlPosicionable(pacman, new Punto(x,y));
        }
        Obstaculo obstaculo = servicio.obtenerObstaculoMasCercano(this, distanciaDeColision.doubleValue());
        if (obstaculo != null){
            this.revertirMovimiento();
            if ((posXInicial == this.x) && (posYInicial == this.y)) {
                this.buscarMovimientoPosible(servicio);
            }
        }
    }

    private void buscarMovimientoPosible(ServicioJuego servicio) {
        int sentido = 1;
        Obstaculo obstaculo=null;
        while (sentido < 4) {
            this.moverSegunSentido(sentido);
            this.x += this.sentidoX;
            this.y += this.sentidoY;
            obstaculo = servicio.obtenerObstaculoMasCercano(this, distanciaDeColision.doubleValue());
            if (obstaculo == null) {
                sentido = 5; // pudo mover salgo del ciclo
            } else {
                this.revertirMovimiento();
                sentido++;
            }
        }
    }

    private void moverSegunSentido(int sentido) {
        switch (sentido) {
            case 1:
                this.cambiarSentidoAVerticalAbajo();
                break;
            case 2:
                this.cambiarSentidoAHorizontalIzquierda();
                break;
            case 3:
                this.cambiarSentidoAVerticalArriba();
                break;
            case 4:
                this.cambiarSentidoAHorizontalDerecha();
                break;
            default:
                break;
        }
    }

    private int calcularDistanciaAlPosicionable(Posicionable objeto, Punto p) {
        int distanciaHorizontal = Math.abs(p.getX() - objeto.getX());
        int distanciaVertical = Math.abs(p.getY() - objeto.getY());
        return distanciaHorizontal + distanciaVertical;
    }

    private void revertirMovimiento() {
        this.x -= this.sentidoX;
        this.y -= this.sentidoY;
    }

    public void moverHorizontalmente(int distancia) {
        if (distancia > 0) {
            this.cambiarSentidoAHorizontalIzquierda();
        } else {
            this.cambiarSentidoAHorizontalDerecha();
        }
    }

    public void moverVerticalmente(int distancia) {
        if (distancia > 0) {
            this.cambiarSentidoAVerticalArriba();
        } else {
            this.cambiarSentidoAVerticalAbajo();
        }
    }

    private void moverHaciaElPunto(Punto p) {
        int deltaX,deltaY;

        deltaX = this.x - p.getX();
        deltaY = this.y - p.getY();

        if (deltaX > 0) {
            cambiarSentidoAHorizontalIzquierda();
        }
        else if (deltaX < 0) {
            cambiarSentidoAHorizontalDerecha();
        }

        if (deltaY > 0) {
            cambiarSentidoAVerticalArriba();
        }
        else if (deltaY < 0) {
            cambiarSentidoAVerticalAbajo();
        }
    }

    private boolean meAproximeAlPacman(int distanciaInicial, int distanciaActual) {
        return distanciaInicial > distanciaActual;
    }

    private boolean meAlejeDelPacman(int distanciaInicial, int distanciaActual) {
        return distanciaInicial < distanciaActual;
    }
}
